#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>


//目的：使用应用代码控制gpio输入，输出

//  5.1. 使用sysfs控制gpio  
//  通过GPIO输出应用程序控制GPIO口输出高低电平，以此来控制LED灯的亮灭。  命令导出gpio通过代码实现

//  echo 233 > export     导出这个gpio口
//  echo out > gpio233/direction    设置为输出模式 
 // echo in > gpio233/direction       设置为输入模式 
// cat  gpio233/direction 
 // echo 1> gpio233 /value    把电平拉高在 用万用表看一下
//  cat gpio233/value   输出电平是0     测试写入是否成功



// 上面通过代码方式实现
// 如果这个gpio已经被使用,echo 233 > export , 出现错误Device or resource busy 
// 需要在内核中描述改gpio的地方，注释掉，或者   status = "disabled"; 
 
int fd;                   // 文件描述符
int ret;                  // 返回值
char gpio_path[100];      // GPIO路径
int len;                  // 字符串长度
char file_path[100]; // 文件路径
char buf[2];         // 用于读取 GPIO 值的缓冲区


// 导出GPIO引脚     
int gpio_export(char *argv)
{
    fd = open("/sys/class/gpio/export", O_WRONLY); // 打开export文件
    if (fd < 0)
    {
        printf("open /sys/class/gpio/export error \n"); // 打开文件失败
        return -1;
    }
    len = strlen(argv); // 获取参数字符串的长度
  //     echo 226 > export    这个函数相当于这条命令

    ret = write(fd, argv, len); // 将参数字符串写入文件，导出GPIO引脚
    if (ret < 0)
    {
        printf("write /sys/class/gpio/export error \n"); // 写入文件失败
        return -2;
    }
    close(fd); // 关闭文件
}
 
// 取消导出GPIO引脚
int gpio_unexport(char *argv)
{
    fd = open("/sys/class/gpio/unexport", O_WRONLY); // 打开unexport文件
    if (fd < 0)
    {
        printf("open /sys/class/gpio/unexport error \n"); // 打开文件失败
        return -1;
    }
    len = strlen(argv); // 获取参数字符串的长度
    //    echo 226 > unexport
    ret = write(fd, argv, len); // 将参数字符串写入文件，取消导出GPIO引脚
    if (ret < 0)
    {
        printf("write /sys/class/gpio/unexport error \n"); // 写入文件失败
        return -2;
    }
    close(fd); // 关闭文件
}
 
// 控制GPIO引脚的属性
int gpio_ctrl(char *arg, char *val)
{
    char file_path[100]; // 文件路径
    sprintf(file_path, "%s/%s", gpio_path, arg); // 构建文件路径，格式为“gpio_path/arg”
    fd = open(file_path, O_WRONLY); // 打开文件
    if (fd < 0)
    {
        printf("open file_path error : %s\n" , file_path); // 打开文件失败
        return -1;
    }
    len = strlen(val); // 获取参数字符串的长度
    ret = write(fd, val, len); // 将参数字符串写入文件，控制GPIO引脚的属性
    if (ret < 0)
    {
        printf("write file_path error\n"); // 写入文件失败
        return -2;
    }
    close(fd); // 关闭文件
}


// 读取 GPIO 引脚的值
int gpio_read_value(char *arg)
{
    sprintf(file_path, "%s/%s", gpio_path, arg); // 构建文件路径，格式为 "gpio_path/arg"
    fd = open(file_path, O_RDONLY);              // 打开文件
    if (fd < 0)
    {
        printf("open file_path error\n"); // 打开文件失败
        return -1;
    }
    ret = read(fd, buf, 1); // 读取文件内容到缓冲区
    if (!strcmp(buf, "1"))
    {
        printf("The value is high\n"); // GPIO 引脚值为高电平
        return 1;
    }
    else if (!strcmp(buf,"0"))
    {
        printf("The value is low\n"); // GPIO 引脚值为低电平
        return 0;
    }
    
    close(fd); // 关闭文件
    return -1;
    
}

/**
 * gpio设置输出,拉io口点灯
 */


//  ./gpioctrl.o  147 0   红灯灭
// ./gpioctrl.o  147 1  红灯亮

int main01(int argc, char *argv[]) // 主函数
{
    sprintf(gpio_path, "/sys/class/gpio/gpio%s", argv[1]); // 构建GPIO路径，格式为“/sys/class/gpio/gpio引脚号”
    if (access(gpio_path, F_OK)) // 检查GPIO路径是否存在  ,判断是否已经到处了，导出了就不用了
    {
        gpio_export(argv[1]); // 不存在则导出GPIO引脚
    }
    else
    {
        gpio_unexport(argv[1]); // 存在则取消导出GPIO引脚
    }
 
    gpio_ctrl("direction", "out"); // 配置GPIO为输出模式
    gpio_ctrl("value", argv[2]);   // 控制GPIO输出高低电平
 
    gpio_unexport(argv[1]); // 最后取消导出GPIO引脚
 
    return 0; // 返回0表示程序正常退出
}

/**
 *  gpio设置输入，读io口
 * 
 * ./gpioctrl.o  147
 * 
 *  测试，用杜邦线把这个引脚开发板上的引出的3.3V连接，
 *                                       接地
 */


int main(int argc, char *argv[]) // 主函数
{
    int value;
    sprintf(gpio_path, "/sys/class/gpio/gpio%s", argv[1]); // 构建 GPIO 路径，格式为 "/sys/class/gpio/gpio引脚号"
    if (access(gpio_path, F_OK))                           // 检查 GPIO 路径是否存在
    {
        gpio_export(argv[1]); // 不存在则导出 GPIO 引脚
    }
    else
    {
        gpio_unexport(argv[1]); // 存在则取消导出 GPIO 引脚
    }

    gpio_ctrl("direction", "in");       // 配置 GPIO 为输入模式
    
    value = gpio_read_value("value");   // 读取 GPIO 引脚的值
    printf("The value is %d\n", value); // 打印读取的 GPIO 引脚的值
    gpio_unexport(argv[1]);             // 最后取消导出 GPIO 引脚

    return 0; // 返回 0 表示程序正常退出
}